package com.company.homework;


import java.math.BigDecimal;
import java.util.*;

public final class TransitionImp implements Transition {
    private String expression;
    private String infixExpression=null;
    private String postfixExpression=null;
    private Stack<Character> operatorStack;

    private static final Set<Character> operators;

    static {
        operators =new HashSet<>();
        operators.add('+');
        operators.add('-');
        operators.add('*');
        operators.add('/');
    }

    /**
     * 只允许以一个文件构造此函数
     * @param expression 一条待处理的算式
     */
    public TransitionImp(String expression){
        operatorStack=new Stack<>();
        this.expression=expression;
    }

    /**
     * 读取算式内容,过滤空白符并把中缀表达式放入到infixExpression中
     */
    private void readExpression(){
        Scanner input=new Scanner(expression);
        StringBuilder builder=new StringBuilder();

        while (input.hasNext()){
            builder.append(input.next());
        }

        infixExpression=builder.toString();
    }

    /**
     * 把中缀表达式转换为后缀表达式
     * @return 返回后缀表达式
     */
    private String transition() throws Exception{
        int pre=0;
        StringBuilder postfixExpression=new StringBuilder();

        for(int i=0;i<infixExpression.length();i++) {
            char ch = infixExpression.charAt(i);

            if (!isOperator(ch)) continue;

            //文法校验运算
            String number = infixExpression.substring(pre, i);
            grammerCheck(ch, pre, i, number);
            pre = i + 1;
            if (!number.equals("")) {
                postfixExpression.append(number).append(" ");
            }

            //栈运算
            if (ch == '(') {
                operatorStack.push('(');
            } else if (ch == ')') {
                processRightBracket(postfixExpression);
            } else {
                processOperator(ch,postfixExpression);
            }
        }

        //最后的操作
        String number=infixExpression.substring(pre,infixExpression.length());

        if(!number.equals("")){
            postfixExpression.append(number).append(' ');
        }

        grammerCheck('$',pre,infixExpression.length(),number);
        while (!operatorStack.isEmpty()){
            if(!operators.contains(operatorStack.peek())){
                throw new Exception("括号不匹配");
            }

            postfixExpression.append(operatorStack.pop()).append(' ');
        }

        operatorStack=null;

        return postfixExpression.toString();
    }

    /**
     * 进行文法校验
     * @param ch 运算符
     * @param index 运算符下标
     * @param number 截取出来的数字
     * @throws Exception 抛出语法相关的异常
     */
    private void grammerCheck(char ch,int pre, int index, String number)throws Exception{
        //如果ch为一个左括号，则他的上一个字符必须要是一个左括号或者是运算符，或者为空
        if(ch=='('){
            if(index!=0&&infixExpression.charAt(index-1)!='('
                    &&!operators.contains(infixExpression.charAt(index-1))){
                throw new Exception("在位置"+index+"处左括号使用不正确");
            }

            return;
        }
        //当读取不到数字的时候
        if(number.equals("")){
            //此时ch左侧必须为一个右括号，否则产生错误
            if(index==0||infixExpression.charAt(index-1)!=')') {
                throw new Exception("在位置" + index+"处的运算符使用不正确");
            }
        }else if(!digitCheck(number)) {
            throw new Exception("在位置"+pre+"到位置"+(index-1)+"的数字有误");
        }
    }

    private int priority(char ch){
        switch (ch){
            case '(':return 0;
            case '-':
            case '+':return 1;
            case '*':
            case '/':return 2;
        }

        return -1;
    }

    /**
     * 判断是否为运算符
     * @return 如果是则返回ture，否则返回false
     */
    private boolean isOperator(char ch){
        return operators.contains(ch)||ch=='('||ch==')';
    }

    /**
     * 处理一个运算符
     * @param ch 运算符
     */
    private void processOperator(char ch,StringBuilder postfixExpression){
        if(operatorStack.isEmpty()||priority(ch)>priority(operatorStack.peek())){
            operatorStack.push(ch);
        }else {
            popOperators(postfixExpression);
            operatorStack.push(ch);
        }
    }

    /**
     * 进行一次弹出运算符的运算
     */
    private void popOperators(StringBuilder postfixExpression){
        while (!operatorStack.isEmpty()&&operatorStack.peek()!='('){
            postfixExpression.append(operatorStack.pop()).append(' ');
        }
    }

    /**
     * 处理一个右括号
     */
    private void processRightBracket(StringBuilder postfixExpression) throws Exception{
        popOperators(postfixExpression);

        if(!operatorStack.isEmpty()&&operatorStack.peek()=='('){
            operatorStack.pop();
        }else {
            throw new Exception("括号不匹配");
        }
    }


    /**
     * 数字校验，检验一个字符串是否可以转化为数字
     * @param num 待转化的字符串
     * @return 若能转化则返回true否则返回false
     */
    private boolean digitCheck(String num){
        try{
            new BigDecimal(num);
            return true;
        }catch (Exception ex){
            return false;
        }
    }

    @Override
    public String getPostfixExpression() throws Exception{
        if(postfixExpression!=null) return postfixExpression;

        if(infixExpression!=null){
            postfixExpression=transition();
        }else {
            readExpression();
            postfixExpression=transition();
        }

        return postfixExpression;
    }

    @Override
    public void printPostfixExpression() {
        try {
            getPostfixExpression();

            System.out.println("中缀表达式：");
            System.out.println(infixExpression);
            System.out.println("后缀表达式：");
            System.out.println(getPostfixExpression());
            System.out.println();
        }catch (Exception ex){
            System.out.println(infixExpression);
            System.out.println(ex.getMessage());
            System.out.println();
        }
    }

    public String getInfixExpression(){
        return infixExpression;
    }
}
